﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace CommonWFLibrary
{
    /// <summary>
    /// Abstract class to hold onto the various call-back paterns that we support.
    /// Originally required to support two classes of callback, but using MethodInfo
    /// those were combined into a single MAIWithArg. Left to support future expansion...
    /// </summary>
    [Serializable]
    public abstract class MethodArgumentInterface
    {
        /// <summary>
        /// Prepare for a callback - executed when the activity is in the current
        /// context. Returns a serializable object (or null) that can can be passed
        /// for parsing.
        /// </summary>
        /// <returns></returns>
        public abstract object PrepForCall(object activity);

        /// <summary>
        /// Runs the actual work. Returns an object that can be passed back to the
        /// activity for unpacking.
        /// </summary>
        /// <param name="c">Context to use to indicate you are done with this task</param>
        /// <param name="arg">args to be passed in from external workflow</param>
        /// <param name="context">Context to be passed in for multiple-runs</param>
        /// <returns>Updated context to be saved if we have to run again</returns>
        public abstract object DoWork(LongRunningContext c, object arg, object context);

        /// <summary>
        /// Called to take the object from DoWork and put it back!
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public abstract void FinishUpCall(object activity, object arg);
    }

    /// <summary>
    /// Implementation of the callback.
    /// </summary>
    [Serializable]
    public class MAIWithArg : MethodArgumentInterface
    {
        public MethodInfo _run_callback;
        public MethodInfo _gather_callback;
        public MethodInfo _distribute_callback;

        public override object PrepForCall(object activity)
        {
            if (_gather_callback != null)
            {
                return _gather_callback.Invoke(activity, null);
            }
            return null;
        }

        /// <summary>
        /// If the Run takes no arguments, then don't try to pass it any!
        /// Note that _run_callback can't be null. :-)
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public override object DoWork(LongRunningContext c, object arg, object context)
        {
            object result = null;
            if (_run_callback.GetParameters().Length == 0)
            {
                result = _run_callback.Invoke(null, null);
            }
            else
            {
                result = _run_callback.Invoke(null, new object[] { arg });
            }
            c.Complete(result);
            return null;
        }

        public override void FinishUpCall(object activity, object arg)
        {
            if (_distribute_callback != null)
            {
                _distribute_callback.Invoke(activity, new object[] { arg });
            }
        }
    }

    [Serializable]
    public class MAIAsyncWithArg : MethodArgumentInterface
    {
        public MethodInfo _run_async_callback;
        public MethodInfo _gather_callback;
        public MethodInfo _distribute_callback;

        public override object PrepForCall(object activity)
        {
            if (_gather_callback != null)
            {
                return _gather_callback.Invoke(activity, null);
            }
            return null;
        }

        public override object DoWork(LongRunningContext c, object arg, object context)
        {
            object result = null;
            switch (_run_async_callback.GetParameters().Length)
            {
                case 1:
                    _run_async_callback.Invoke(null, new object[] { c });
                    break;
                case 2:
                    _run_async_callback.Invoke(null, new object[] { c, arg });
                    break;
                case 3:
                    result = _run_async_callback.Invoke(null, new object[] { c, arg, context });
                    break;
                default:
                    throw new NotImplementedException("Unknown type of parameters for the run starter method - how did this happen!?");
            }
            return result;
        }

        /// <summary>
        /// Send the results back to the activity
        /// </summary>
        /// <param name="activity"></param>
        /// <param name="arg"></param>
        public override void FinishUpCall(object activity, object arg)
        {
            if (_distribute_callback != null)
            {
                _distribute_callback.Invoke(activity, new object[] { arg });
            }
        }
    }


}
